package org.jdna.minecraft.tools;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.SystemColor;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class MainFrame extends JFrame implements IStatusHandler {
	private static final String MODS_URL = "http://modmanager-for-minecraft.googlecode.com/svn/trunk/MincraftModManager/src/res/xml/mods.xml";
	private Logger log;
	private static final long serialVersionUID = 1L;
	private JPanel contentPane;
	private JTable table;
	private Preferences prefs;
	private JButton btnInstallMods;
	private JMenu mnMods;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InstantiationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (UnsupportedLookAndFeelException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainFrame frame = new MainFrame();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public MainFrame() {
		prefs = Application.getInstance().getPreferences();
		log = Logger.getLogger(this.getClass());

		setIconImage(Toolkit.getDefaultToolkit().getImage(
				MainFrame.class.getResource("/res/icons/icon_256.png")));
		setTitle("Stuckless Mod Manager - " + Version.VERSION);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 603, 402);

		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);

		JMenu mnFile = new JMenu("File");
		menuBar.add(mnFile);

		JMenuItem mntmAddMod = new JMenuItem("Add Mods...");
		mntmAddMod.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				addModAction();
			}
		});
		mntmAddMod.setIcon(Application.icon("list-add"));
		mnFile.add(mntmAddMod);

		JMenuItem mntmExit = new JMenuItem("Exit");
		mntmExit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				exit();
			}
		});
		mntmExit.setIcon(Application.icon("system-log-out"));

		JMenuItem mntmClearMods = new JMenuItem("Clear Mods");
		mntmClearMods.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				((DefaultTableModel) table.getModel()).getDataVector().clear();
				table.repaint();
			}
		});
		mntmClearMods.setIcon(Application.icon("list-remove"));
		mnFile.add(mntmClearMods);

		JMenuItem mntmPreferences = new JMenuItem("Preferences");
		mnFile.add(mntmPreferences);
		mntmPreferences.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				PreferencesDialog dialog = new PreferencesDialog();
				dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
				dialog.setVisible(true);
			}
		});
		mntmPreferences.setIcon(Application.icon("applications-system"));

		JMenuItem mntmRestore = new JMenuItem("Restore...");
		mntmRestore.setIcon(Application.icon("edit-undo"));
		mntmRestore.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				RestoreDialog dialog = new RestoreDialog();
				dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
				dialog.setVisible(true);
			}
		});
		mnFile.add(mntmRestore);

		JSeparator separator = new JSeparator();
		mnFile.add(separator);
		mnFile.add(mntmExit);

		mnMods = new JMenu("Mods");
		menuBar.add(mnMods);

		JMenuItem mntmGotoMinecraftMods = new JMenuItem("Minecraft Mods Forum");
		mntmGotoMinecraftMods.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Application
						.getInstance()
						.openUrl(
								"http://www.minecraftforum.net/forum/51-released-mods/");
			}
		});
		mntmGotoMinecraftMods.setIcon(Application.icon("internet-web-browser"));
		mnMods.add(mntmGotoMinecraftMods);

		JMenuItem mntmRefreshModList = new JMenuItem("Refresh Mod List");
		mntmRefreshModList.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				refreshMods(MODS_URL);
			}
		});
		mntmRefreshModList.setIcon(Application.icon("view-refresh"));
		mnMods.add(mntmRefreshModList);

		JSeparator separator_1 = new JSeparator();
		mnMods.add(separator_1);

		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		GridBagLayout gbl_contentPane = new GridBagLayout();
		gbl_contentPane.columnWidths = new int[] { 0, 0 };
		gbl_contentPane.rowHeights = new int[] { 0, 0, 0, 0 };
		gbl_contentPane.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
		gbl_contentPane.rowWeights = new double[] { 0.0, 1.0, 0.0,
				Double.MIN_VALUE };
		contentPane.setLayout(gbl_contentPane);

		JPanel panel = new JPanel();
		panel.setBorder(new LineBorder(SystemColor.windowBorder, 2, true));
		GridBagConstraints gbc_panel = new GridBagConstraints();
		gbc_panel.fill = GridBagConstraints.BOTH;
		gbc_panel.insets = new Insets(0, 0, 5, 0);
		gbc_panel.gridx = 0;
		gbc_panel.gridy = 0;
		contentPane.add(panel, gbc_panel);
		GridBagLayout gbl_panel = new GridBagLayout();
		gbl_panel.columnWidths = new int[] { 0, 0, 0 };
		gbl_panel.rowHeights = new int[] { 0, 0 };
		gbl_panel.columnWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE };
		gbl_panel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
		panel.setLayout(gbl_panel);

		JTextPane txtpnDragModsFrom = new JTextPane();
		txtpnDragModsFrom.setForeground(Color.GRAY);
		GridBagConstraints gbc_txtpnDragModsFrom = new GridBagConstraints();
		gbc_txtpnDragModsFrom.fill = GridBagConstraints.HORIZONTAL;
		gbc_txtpnDragModsFrom.insets = new Insets(0, 0, 0, 5);
		gbc_txtpnDragModsFrom.gridx = 0;
		gbc_txtpnDragModsFrom.gridy = 0;
		panel.add(txtpnDragModsFrom, gbc_txtpnDragModsFrom);
		txtpnDragModsFrom.setFont(new Font("Dialog", Font.BOLD | Font.ITALIC,
				12));
		txtpnDragModsFrom.setBackground(Color.WHITE);
		txtpnDragModsFrom.setEditable(false);
		txtpnDragModsFrom
				.setText("Drag Mods from your file manager to the Table below and click 'Install Mods' or use File -> Add Mods from the menu to add mods");

		{
			// JLabel lblTest = new JLabel("test");
			ImagePanel img = new ImagePanel(
					Application.imageRes("res/donate.png"));
			GridBagConstraints gbc_lblTest = new GridBagConstraints();
			gbc_lblTest.gridx = 1;
			gbc_lblTest.gridy = 0;
			panel.add(img, gbc_lblTest);
			img.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					Application.getInstance().openUrl(prefs.getDonateLink());
				}
			});
		}

		JScrollPane scrollPane = new JScrollPane();
		setupDND(scrollPane);
		GridBagConstraints gbc_scrollPane = new GridBagConstraints();
		gbc_scrollPane.fill = GridBagConstraints.BOTH;
		gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
		gbc_scrollPane.gridx = 0;
		gbc_scrollPane.gridy = 1;
		contentPane.add(scrollPane, gbc_scrollPane);

		table = new JTable();
		setupTable(table);
		scrollPane.setViewportView(table);

		btnInstallMods = new JButton("Install Mods");
		btnInstallMods.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				installMods();
			}
		});
		btnInstallMods.setIcon(Application.icon("go-next"));
		btnInstallMods.setVerticalTextPosition(SwingConstants.CENTER);
		btnInstallMods.setHorizontalTextPosition(SwingConstants.LEFT);
		GridBagConstraints gbc_btnInstallMods = new GridBagConstraints();
		gbc_btnInstallMods.gridx = 0;
		gbc_btnInstallMods.gridy = 2;
		contentPane.add(btnInstallMods, gbc_btnInstallMods);

		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				loadModMenu(mnMods);
			}
		});
	}

	protected void refreshMods(String modsUrl) {
		while (true) {
			JMenuItem mi = mnMods.getItem(mnMods.getItemCount() - 1);
			if (mi == null)
				break;
			mnMods.remove(mnMods.getItemCount() - 1);
		}

		try {
			log.info("Loading mods from " + modsUrl);
			URL url = new URL(modsUrl);
			URLConnection conn = url.openConnection();
			conn.setUseCaches(false);

			SAXReader reader = new SAXReader();
			Document document = reader.read(conn.getInputStream());
			Element root = document.getRootElement();

			for (Iterator i = root.elementIterator("mod"); i.hasNext();) {
				final Element mod = (Element) i.next();
				JMenuItem mi = new JMenuItem(mod.element("name").getTextTrim());
				mi.setIcon(Application.icon("applications-other"));
				mi.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						Application.getInstance().openUrl(
								mod.element("siteUrl").getTextTrim());
					}
				});
				mnMods.add(mi);
			}
		} catch (Throwable t) {
			t.printStackTrace();
			log.warn("Failed to dynamically add mod links");
		}
	}

	protected void loadModMenu(JMenu mnMods) {
		refreshMods(prefs.getModsUrl().toExternalForm());
	}

	protected void exit() {
		System.exit(0);
	}

	public void addModAction() {
		JFileChooser fc = new JFileChooser(prefs.getMinecraftModsDir());
		fc.setDialogTitle("Select Mod");
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setMultiSelectionEnabled(true);
		fc.setFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "Mod Files";
			}

			@Override
			public boolean accept(File f) {
				return f.isDirectory()
						|| f.getName().toLowerCase().endsWith(".zip")
						|| f.getName().toLowerCase().endsWith(".rar");
			}
		});
		fc.showOpenDialog(this);
		File files[] = fc.getSelectedFiles();
		if (files != null) {
			for (File f : files) {
				prefs.setMinecraftModsDir(f.getParentFile());
				addModFile(f);
			}
		}
	}

	private void addModFile(File file) {
		log.info("Adding File: " + file.getAbsolutePath());
		DefaultTableModel dtm = (DefaultTableModel) table.getModel();
		dtm.addRow(new Object[] { true, file.getAbsolutePath(), "" });
	}

	private void setupTable(final JTable table) {
		DefaultTableModel model = new DefaultTableModel(new String[] { "",
				"Mod", "Status" }, 0) {
			@Override
			public boolean isCellEditable(int row, int column) {
				return false;
			}
		};
		table.setModel(model);
		table.getColumnModel().getColumn(0)
				.setCellEditor(table.getDefaultEditor(Boolean.class));
		table.getColumnModel().getColumn(0)
				.setCellRenderer(table.getDefaultRenderer(Boolean.class));
		table.getColumnModel().getColumn(0).setPreferredWidth(16);
		table.getColumnModel().getColumn(1).setPreferredWidth(400);

		// hide the enabled column for now
		table.getColumnModel()
				.removeColumn(table.getColumnModel().getColumn(0));

		// add context menu
		final JPopupMenu ctxMenu = new JPopupMenu();
		JMenuItem del = new JMenuItem("Remove Mod");
		del.setIcon(Application.icon("list-remove"));
		del.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				((DefaultTableModel) table.getModel()).removeRow(table.getSelectedRow());
			}
		});
		ctxMenu.add(del);

		table.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				showPopup(e);
			}

			public void mouseReleased(MouseEvent e) {
				showPopup(e);
			}

			private void showPopup(MouseEvent e) {
				if (e.isPopupTrigger()) {
					ListSelectionModel selectionModel = table.getSelectionModel();
					selectionModel.setSelectionInterval(table.rowAtPoint(e.getPoint()), table.rowAtPoint(e.getPoint()));
					ctxMenu.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		});

		setupDND(table);
	}

	private void setupDND(JComponent comp) {
		comp.setDropTarget(new DropTarget() {
			private static final long serialVersionUID = 1L;

			@Override
			public synchronized void drop(DropTargetDropEvent dtde) {
				dtde.acceptDrop(dtde.getDropAction());
				acceptTranserable(dtde.getTransferable());
				dtde.dropComplete(true);
			}
		});
	}

	private void acceptTranserable(Transferable transferable) {
		try {
			String sfiles = (String) transferable
					.getTransferData(DataFlavor.stringFlavor);
			String[] files = sfiles.split("\n");
			for (String f : files) {
				addModFile(new File(URI.create(f.trim())));
			}
		} catch (UnsupportedFlavorException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	protected void installMods() {
		if (table.getRowCount() == 0) {
			JOptionPane.showMessageDialog(this,
					"You must add at least one mod", "Install Message",
					JOptionPane.WARNING_MESSAGE);
			return;
		}

		try {
			ModInstallerDialog dialog = new ModInstallerDialog();
			dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			dialog.setVisible(true);

			List<File> files = new ArrayList<File>();
			for (int i = 0; i < table.getRowCount(); i++) {
				files.add(new File((String) table.getModel().getValueAt(i, 1)));
			}

			dialog.performUpdate(files, this);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void updateStatus(File f, boolean state, String msg) {
		for (int i = 0; i < table.getRowCount(); i++) {
			String val = (String) table.getValueAt(i, 1);
			if (val != null && val.equals(f.getAbsolutePath())) {
				table.setValueAt(msg, i, 2);
				break;
			}
		}
	}

	@Override
	public void installed(File f) {
		log.info("installed mod " + f);
		updateStatus(f, true, "OK");
	}

	@Override
	public void fail(File f, Throwable t) {
		log.warn("failed to install file: " + f, t);
		updateStatus(f, false, "FAILED");
	}
}
